using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Index;
using Query;
using Index.PostingLists;
using Index.PostingLists.Entry;

namespace Query.Queries
{
    public class BooleanQuery
    {
        public ArrayList queryTree;
        public readonly string query;
        private WikiIndex index;


        
  
        public BooleanQuery(string query, WikiIndex ind)
        {
            index = ind;
            this.query = query;
            this.queryTree = parseQuery();
        }


        public void setIndex(WikiIndex ind)
        {
            index = ind;
        }

        public List<int> processThisQuery()
        {
            DocumentPositionList listP = new DocumentPositionList();
            DocumentFrequencyList listF = new DocumentFrequencyList();
            Boolean freq = false;
            Boolean post = false;

            if (index.Config.IndexType == IndexType.Positional)
            {
                listP = new DocumentPositionList();
                listP = (DocumentPositionList)(index.GetPostingList((string)queryTree[0])) ;
                post = true;
            }

            if (index.Config.IndexType == IndexType.Frequencial)
            {
                listF = new DocumentFrequencyList();
                listF = (DocumentFrequencyList)(index.GetPostingList((string)queryTree[0])) ;
                freq = true;
            }

            
            int currentOperator = Operator.OP_NONE;

            for(int i = 1; i < queryTree.Count; i++)
            {
                if (queryTree[i].GetType() == typeof(string) && currentOperator == Operator.OP_AND)
                {
                    if (post)
                    {
                        DocumentPositionList[] lists = new DocumentPositionList[2];
                        lists[0] = (DocumentPositionList)index.GetPostingList((string)queryTree[i]);
                        lists[1] = listP;
                        listP = Intersection.Intersect(lists)[0];
                    }
                    else
                    {
                        DocumentFrequencyList[] lists = new DocumentFrequencyList[2];
                        lists[0] = (DocumentFrequencyList)index.GetPostingList((string)queryTree[i]);
                        lists[1] = listF;
                        listF = Intersection.Intersect(lists)[0];
                    }
                }
                else if (queryTree[i].GetType() == typeof(string) && currentOperator == Operator.OP_OR)
                {
                    if (post)
                    {
                        DocumentPositionList[] lists = new DocumentPositionList[2];
                        lists[0] = (DocumentPositionList)index.GetPostingList((string)queryTree[i]);
                        lists[1] = listP;
                        listP = Sum.convertPost(Sum.sum(lists));
                    }
                    else
                    {
                        DocumentFrequencyList[] lists = new DocumentFrequencyList[2];
                        lists[0] = (DocumentFrequencyList)index.GetPostingList((string)queryTree[i]);
                        lists[1] = listF;
                        listF = Sum.convertFreq(Sum.sum(lists));
                    }
                }
                else if (queryTree[i].GetType() == typeof(Operator)
                    && ((Operator)queryTree[i]).type == Operator.OP_OR)
                        currentOperator = Operator.OP_OR;
                else if (queryTree[i].GetType() == typeof(Operator)
                    && ((Operator)queryTree[i]).type == Operator.OP_AND)
                        currentOperator = Operator.OP_AND;
                else if(currentOperator == Operator.OP_OR)
                {
                    if (post)
                    {
                        DocumentPositionList[] lists = new DocumentPositionList[2];
                        lists[0] = Sum.convertPost(processSubQuery((BooleanQuery)queryTree[i]));
                        lists[1] = listP;
                        listP = Sum.convertPost(Sum.sum(lists));
                    }
                    else
                    {
                        DocumentFrequencyList[] lists = new DocumentFrequencyList[2];
                        lists[0] = Sum.convertFreq(processSubQuery((BooleanQuery)queryTree[i]));
                        lists[1] = listF;
                        listF = Sum.convertFreq(Sum.sum(lists));
                    }
                }
                else
                {
                    if (post)
                    {
                        DocumentPositionList[] lists = new DocumentPositionList[2];
                        lists[0] = Sum.convertPost(processSubQuery((BooleanQuery)queryTree[i]));
                        lists[1] = listP;
                        listP = Intersection.Intersect(lists)[0];
                    }
                    else
                    {
                        DocumentFrequencyList[] lists = new DocumentFrequencyList[2];
                        lists[0] = Sum.convertFreq(processSubQuery((BooleanQuery)queryTree[i]));
                        lists[1] = listF;
                        listF = Intersection.Intersect(lists)[0];
                    }

                }

            }

            if (freq)
                return Sum.convert(listF);
            else
                return Sum.convert(listP);
        }

        public List<int> getDocumentIdList(string term)
        {
            object documentList = index.GetPostingList(term);

            if (documentList == null)
                return null;

            List<int> result = new List<int>();

            if (index.Config.IndexType == IndexType.Positional)
            {
                DocumentPositionList list = (DocumentPositionList)documentList;
                foreach (PositionEntry entry in list)
                    result.Add(entry.DocumentId);
            }
            if (index.Config.IndexType == IndexType.Frequencial)
            {
                DocumentFrequencyList list = (DocumentFrequencyList)documentList;

                foreach (FrequencyEntry entry in list)
                    result.Add(entry.DocumentId);
            }
            return result;
        }


        public List<int> processSubQuery(BooleanQuery q)
        {
            return q.processThisQuery();
        }


        // zakladam ze query wyglada tak "term" op "term"
        // gdzie op = AND, OR + nawiasy, wazne ze term jest w ""
        public ArrayList parseQuery()
        {
            queryTree = new ArrayList();

            StringBuilder term = new StringBuilder();
            StringBuilder subQuery = new StringBuilder();

            int index = 0;
            int raceCounter = 0;
            Boolean termReading = false;
            Boolean subQueryReading = false;

            while (index < query.Length)
            {
                if (subQueryReading == false && termReading == false && query[index] == '(')
                {
                    raceCounter = 1;
                    index++;
                    subQuery = new StringBuilder();
                    subQueryReading = true;
                }
                else if (subQueryReading == true && query[index] == '(')
                {
                    raceCounter++;
                    index++;
                    subQuery.Append('(');
                }
                // skonczylismy czytac podzapytanie
                else if (subQueryReading == true && query[index] == ')' && raceCounter == 1)
                {
                    raceCounter = 0;
                    subQueryReading = false;
                    BooleanQuery sub = new BooleanQuery(subQuery.ToString(), this.index);
                    queryTree.Add(sub);
                    index++;
                }
                else if (subQueryReading == true && query[index] == ')')
                {
                    raceCounter--;
                    index++;
                    subQuery.Append(')');
                    //Console.Write("\n\n" + subQuery.ToString() + "\n\n");
                }
                else if (subQueryReading == true)
                {
                    subQuery.Append(query[index]);
                    index++;
                }
                // oznacza ze rozpoczynamy czytanie termu
                else if (query[index] == '\"' && termReading == false)
                {
                    term = new StringBuilder();
                    termReading = true;
                    index++;
                }
                else if (query[index] == '\"' && termReading == true)
                {
                    queryTree.Add(term.ToString());
                    termReading = false;
                    index++;
                }
                else if (termReading == true)
                {
                    term.Append(query[index]);
                    index++;
                }
                // oznacza ze zaczelismy czytac podzapytanie 
                else if (query[index] == 'A')
                {
                    if (query.Substring(index, 3) == "AND")
                    {
                        queryTree.Add(new Operator(Operator.OP_AND));
                        index += 3;

                    }
                    else
                    {
                        break;
                    }
                }
                else if (query[index] == 'O')
                {
                    if (query.Substring(index, 2) == "OR")
                    {
                        queryTree.Add(new Operator(Operator.OP_OR));
                        index += 2;
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    index++;
                }
            }
            return queryTree;
        }

        public void printQueryTree(ArrayList q)
        {
            foreach (object elem in q)
            {
                if( elem.GetType() == typeof(BooleanQuery)) 
                {
                    Console.Write('(');
                    printQueryTree( ((BooleanQuery)elem).queryTree );
                    Console.Write(')');
                }
                else
                    Console.Write(elem.ToString() + " ");
            }
        }

    }
}
